package pathfinder

import (
	"encoding/json"
	"math"
	"os"
	"path/filepath"
	"src/zeus/linmath"
	"src/zeus/logger"
	log "src/zeus/logger"
	"strings"
)

// 加载linkpoint
// Point 结构体
type Point struct {
	ID         int16            `json:"id"`
	Pos        *linmath.Vector3 `json:"pos"`
	LinkPoints []int16          `json:"linkid"`
}

type LinkPoints map[int16]*Point

var MapLinkPoints map[string]LinkPoints = make(map[string]LinkPoints)

func InitLinkPoints() bool {
	err := filepath.Walk("../res/pathfinder/linkpoints", func(path string, f os.FileInfo, err error) error {
		if f == nil {
			return nil
		}
		if f.IsDir() {
			return nil
		}
		if strings.HasSuffix(path, ".json") {
			data, err := os.ReadFile(path)
			if err != nil {
				log.Error("ReadFile LinkPoints err: ", err)
			}
			var Points LinkPoints
			err = json.Unmarshal(data, &Points)
			if err != nil {
				log.Error("Unmarshal LinkPoints err: ", err)
			} else {
				_, ok := MapLinkPoints[f.Name()]
				if ok {
					log.Debug("LinkPoints has same filename ", f.Name())
				} else {
					MapLinkPoints[f.Name()] = Points
				}
			}

			log.Debug("LinkPoints path ", path)
		}

		return nil
	})

	if err != nil {
		log.Error("walk Linkpoints file error")
		return false
	}
	return true
}

func GetLinkPoints(key string) (LinkPoints, bool) {
	value, ok := MapLinkPoints[key]
	if ok {
		return value, true
	}
	return nil, false
}

// ------------------以下是树形结构路点寻路------------------------------//
type LinkPointsParams struct {
	start linmath.Vector3
	end   linmath.Vector3
}

func NewLinkPointsFinder(filename string, navgationfinder IPathFinder) *LinkPointsFinder {
	pointsmap, ok := GetLinkPoints(filename + ".json")
	if !ok {
		return nil
	}
	finder := &LinkPointsFinder{
		pointsMap:       pointsmap,
		navgationFinder: navgationfinder,
	}
	return finder
}

type CallFuncPoint func(point *Point)
type CallFuncId func(id int16) bool

// Node结构体
type Node struct {
	point *Point
	next  *Node
}

type Line struct {
	head  *Node
	tail  *Node
	dirty bool
}
type SingleLine []*Point

func (this *Line) Add(point *Point) bool {
	//判断是否已经查找过此节点，如果已经查找过就把此节点设置为false
	for iter := this.head; iter != nil; iter = iter.next {
		if iter.point.ID == point.ID {
			this.dirty = true
			return false
		}
	}
	node := &Node{
		point: point,
		next:  nil,
	}
	if this.head == nil {
		this.head = node
		this.tail = this.head
	} else {
		this.tail.next = node
		this.tail = this.tail.next
	}
	return true
}

func (this *Line) ForeachId(call CallFuncId) bool {
	for _, id := range this.tail.point.LinkPoints {
		if call(id) {
			return true
		}
	}
	return false
}

func (this *Line) ForeachPoint(call CallFuncPoint) {
	for iter := this.head; iter != nil; iter = iter.next {
		call(iter.point)
	}
}

func (this *Line) CopyLine() *Line {
	return &Line{
		head:  this.head,
		tail:  this.tail,
		dirty: false,
	}
}

type LinkPointsFinder struct {
	pointsMap       map[int16]*Point
	navgationFinder IPathFinder
}

func (this *LinkPointsFinder) SetPoints(pointsMap map[int16]*Point) {
	this.pointsMap = pointsMap
}

// GetNear 获取某个点附近的合法位置
// 参数：
//
//	params 检查点 *linmath.Vector3 类型
//
// 返回值：
//
//	pos 路点，注意第一个点就是输入的起始点
//	result 是否能成功
func (this *LinkPointsFinder) GetNear(srcPos *linmath.Vector3) (*linmath.Vector3, bool) {

	return nil, false
}

func (this *LinkPointsFinder) CheckWall(srcPos, dstPos linmath.Vector3) (path []linmath.Vector3, bHaveWall bool) {
	bHaveWall = false
	return
}

func (this *LinkPointsFinder) FindPath(stpos, edpos linmath.Vector3) ([]linmath.Vector3, bool) {
	if this.pointsMap == nil {
		return nil, false
	}

	nStart := this.findNearPoint(stpos)
	nEnd := this.findNearPoint(edpos)
	if nStart.ID == nEnd.ID {
		//path = append(path, nStart.Pos)
		//path = append(path,end)
		return nil, true
	}

	var currentLine *Line
	lineList := make([]*Line, 0, 16)
	lineList = append(lineList, &Line{
		head:  nil,
		tail:  nil,
		dirty: false,
	})
	lineList[0].Add(nStart)
	//此处只能循环50次 如果50次还没找到就放弃
	for loop := 0; loop < 50; loop++ {
		lens := len(lineList)
		for i := 0; i < lens; i++ {
			line := lineList[i]
			//如果此处line里面的结点有重复，此line就废弃
			if line.dirty {
				continue
			}
			//for 循环line里面最后一个节点的相邻节点
			if line.ForeachId(func(id int16) bool {
				if i == 0 {
					currentLine = line
				} else {
					currentLine = line.CopyLine()
					lineList = append(lineList, currentLine)
				}
				currentLine.Add(this.findPoint(id))
				if nEnd.ID == id {
					return true
				}
				return false
			}) {
				//返回path对象
				path := make([]linmath.Vector3, 0, 16)
				currentLine.ForeachPoint(func(point *Point) {
					path = append(path, *point.Pos)
				})
				return path, true
			}
		}
	}
	return nil, false
}

func (this *LinkPointsFinder) findNearPoint(pos linmath.Vector3) *Point {
	if this.pointsMap == nil {
		return nil
	}
	var outPoint *Point = nil
	var shortLength float32 = math.MaxFloat32
	for _, point := range this.pointsMap {
		curLength := point.Pos.Sub(pos).Len()
		if curLength < shortLength {
			outPoint = point
			shortLength = curLength
		}
	}
	return outPoint
}

func (this *LinkPointsFinder) findPoint(id int16) *Point {
	if this.pointsMap == nil {
		return nil
	}
	point, ok := this.pointsMap[id]
	if !ok {
		return nil
	}
	return point
}

// Raycast 射线检测
func (this *LinkPointsFinder) Raycast(params interface{}) (*linmath.Vector3, bool) {
	return nil, false
}

func (this *LinkPointsFinder) RaycastEx(srcPos, dstPos linmath.Vector3) (*linmath.Vector3, bool) {
	return nil, false
}

// 动态阻挡相关
// 增加可旋转的Box阻挡
func (this *LinkPointsFinder) CreateObstacleOBBBox(doorName string, centPos, halfPos linmath.Vector3, YawDegree int32) {
}

// 增加圆柱体阻挡
func (this *LinkPointsFinder) CreateObstacleCylinder(doorName string, centPos linmath.Vector3, radius, height int32) {
}

// 清理掉某个门
func (this *LinkPointsFinder) RemoveObstacle(doorName string) {

}

// 清理全部的门
func (this *LinkPointsFinder) RemoveAllObstacle() {

}

// 更新阻挡信息
func (this *LinkPointsFinder) UpdateObstacle() {

}

func (this *LinkPointsFinder) Destroy() {

}

func (this *LinkPointsFinder) MeshCheckRushPath(srcPos, stpos linmath.Vector3) linmath.Vector3 {
	if tmpPos, ok := this.RaycastEx(srcPos, stpos); !ok {
		logger.Debug(logger.LogKey_MoveSys, " [FindCollection] MeshCheckRushPath 未能检测到墙直接通过 ", stpos)
		return stpos
	} else {
		if tmpPos.IsEqualXZ(srcPos) {
			if path, ok := this.FindPath(srcPos, stpos); ok {
				if len(path) == 2 {
					*tmpPos = path[1]
					logger.Debug(logger.LogKey_MoveSys, " [FindCollection] MeshCheckRushPath 检测到墙 再次尝试走到墙根 ", path[1])
				}
			}
		} else {
			logger.Debug(logger.LogKey_MoveSys, " [FindCollection] MeshCheckRushPath 检测到墙 ", tmpPos)
		}
		stpos = *tmpPos
		return stpos
	}
}
